feat: add $schema field to ToolSchema per SEP-1613#696
Conversation
…zation/deserialization
There was a problem hiding this comment.
Pull request overview
Adds an optional $schema field to ToolSchema so tool input/output schemas can explicitly declare a JSON Schema dialect (defaulting to 2020-12 per SEP-1613 when omitted).
Changes:
- Extend
ToolSchemawithschema: String?serialized as$schema. - Add unit tests covering serialization/deserialization of
$schema. - Update public API dump to reflect the new
ToolSchemashape.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/ToolsTest.kt | Adds round-trip tests ensuring $schema is serialized/deserialized on tool schemas. |
| kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/tools.kt | Introduces ToolSchema.schema with @SerialName("$schema") and updates KDoc. |
| kotlin-sdk-core/api/kotlin-sdk-core.api | Updates the API surface snapshot for the new ToolSchema constructor/components/getter. |
Comments suppressed due to low confidence (1)
kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/tools.kt:100
- ToolSchema’s public data class shape has changed by adding a new constructor parameter/property. This is a binary-incompatible API change for consumers (constructor/copy/componentN signatures change) and also a source-breaking change for any callers using positional arguments. Consider mitigating by adding a deprecated secondary constructor matching the previous parameter order, or moving the new parameter to the end (and/or avoiding a data class change if ABI stability is required).
@Serializable
public data class ToolSchema(
@SerialName("\$schema")
val schema: String? = null,
val properties: JsonObject? = null,
val required: List<String>? = null,
@SerialName("\$defs")
val defs: JsonObject? = null,
) {
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| public final class io/modelcontextprotocol/kotlin/sdk/types/ToolSchema { | ||
| public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema$Companion; | ||
| public fun <init> ()V | ||
| public fun <init> (Lkotlinx/serialization/json/JsonObject;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;)V | ||
| public synthetic fun <init> (Lkotlinx/serialization/json/JsonObject;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V | ||
| public final fun component1 ()Lkotlinx/serialization/json/JsonObject; | ||
| public final fun component2 ()Ljava/util/List; | ||
| public final fun component3 ()Lkotlinx/serialization/json/JsonObject; | ||
| public final fun copy (Lkotlinx/serialization/json/JsonObject;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema; | ||
| public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema;Lkotlinx/serialization/json/JsonObject;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema; | ||
| public fun <init> (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;)V | ||
| public synthetic fun <init> (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V | ||
| public final fun component1 ()Ljava/lang/String; | ||
| public final fun component2 ()Lkotlinx/serialization/json/JsonObject; | ||
| public final fun component3 ()Ljava/util/List; | ||
| public final fun component4 ()Lkotlinx/serialization/json/JsonObject; | ||
| public final fun copy (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema; | ||
| public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema; |
There was a problem hiding this comment.
The updated API dump indicates a binary-breaking public API change to ToolSchema (new ctor signature, new componentN/copy/getSchema). This conflicts with the PR metadata stating “Breaking Changes: none”. Either adjust the PR’s breaking-change note/versioning guidance, or add compatibility shims (e.g., old constructor overload) to preserve ABI/source compatibility if that’s a requirement.
| * Defaults to the JSON Schema 2020-12 dialect when no explicit `$schema` is provided. | ||
| * @property description A human-readable description of the tool and when to use it. | ||
| * Clients can use this to improve the LLM's understanding of available tools. | ||
| * It can be thought of like a "hint" to the model. | ||
| * @property outputSchema An optional JSON Schema object defining the structure of the tool's output | ||
| * returned in the [structuredContent][CallToolResult.structuredContent] field of a [CallToolResult]. | ||
| * Must be an object type schema if provided. | ||
| * Defaults to the JSON Schema 2020-12 dialect when no explicit `$schema` is provided. |
There was a problem hiding this comment.
The KDoc says the schema “defaults to JSON Schema 2020-12” when $schema is absent, but the SDK does not enforce/validate a dialect here—it only (de)serializes data. To avoid implying runtime behavior that isn’t implemented, consider rephrasing to explicitly attribute the default to the MCP/SEP-1613 protocol semantics (i.e., consumers should assume 2020-12 when $schema is missing).
| * Defaults to the JSON Schema 2020-12 dialect when no explicit `$schema` is provided. | |
| * @property description A human-readable description of the tool and when to use it. | |
| * Clients can use this to improve the LLM's understanding of available tools. | |
| * It can be thought of like a "hint" to the model. | |
| * @property outputSchema An optional JSON Schema object defining the structure of the tool's output | |
| * returned in the [structuredContent][CallToolResult.structuredContent] field of a [CallToolResult]. | |
| * Must be an object type schema if provided. | |
| * Defaults to the JSON Schema 2020-12 dialect when no explicit `$schema` is provided. | |
| * Per MCP/SEP-1613 protocol semantics, consumers should assume the JSON Schema 2020-12 | |
| * dialect when no explicit `$schema` is provided. | |
| * @property description A human-readable description of the tool and when to use it. | |
| * Clients can use this to improve the LLM's understanding of available tools. | |
| * It can be thought of like a "hint" to the model. | |
| * @property outputSchema An optional JSON Schema object defining the structure of the tool's output | |
| * returned in the [structuredContent][CallToolResult.structuredContent] field of a [CallToolResult]. | |
| * Must be an object type schema if provided. | |
| * Per MCP/SEP-1613 protocol semantics, consumers should assume the JSON Schema 2020-12 | |
| * dialect when no explicit `$schema` is provided. |
Add optional
$schemafield toToolSchema, establishing JSON Schema 2020-12 as the default dialect per SEP-1613.closes #422
How Has This Been Tested?
unit tests
Breaking Changes
none
Types of changes
Checklist